﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Icinetic.FeedCategorySample.Entities;
using Windows.Data.Html;
using Windows.Storage;
using Windows.Web.Syndication;

namespace Icinetic.FeedCategorySample.Services.Providers
{
    public class FeedsProvider
    {
        private const int cacheUpdateMinutes = 5;

        private SyndicationFeed feed;
        private string category = string.Empty;
        private string feedPath = string.Empty;

        public FeedsProvider() { }

        public FeedsProvider(string feedPath)
        {
            this.feedPath = feedPath;
        }

        public DateTime GetCacheExpiration()
        {
            return DateTime.Now.AddMinutes(cacheUpdateMinutes);
        }

        public async Task<List<RssItem>> Get<T>(string feedUrl, int count = 0, int index = 0) where T : Entity
        {
            List<RssItem> items;

            if (string.IsNullOrWhiteSpace(feedUrl))
                items = await GetFeed(feedPath);
            else
                items = await GetFeed(feedUrl);

            if (count > 0)
                return items.Skip(index * count).Take(count).ToList();
            else
                return items;
        }

        public async Task<List<RssItem>> Get<T>(string feedUrl, string category, int count = 0, int index = 0) where T : Entity
        {
            this.category = category;
            return await Get<T>(feedUrl, count, index);
        }

        private async Task<List<RssItem>> GetFeed(string feedUriString)
        {
            List<RssItem> feedList = new List<RssItem>();

            if (IsLocalPath(feedUriString))
            {
                string feedContent = await ReadFileContent(feedUriString);
                feed = new SyndicationFeed();
                feed.Load(feedContent);
            }
            else
            {
                SyndicationClient client = new SyndicationClient();
                Uri feedUri = new Uri(feedUriString);
                feed = await client.RetrieveFeedAsync(feedUri);
            }

            if (feed != null)
            {
                foreach (SyndicationItem item in feed.Items)
                {
                    RssItem feedData = new RssItem();
                    GetRss(item, feedData);

                    //Código para contemplar distintos formatos de feed
                    //if (Feed.SourceFormat == SyndicationFormat.Atom10)
                    //    GetRss(item, feedData);
                    //else if (Feed.SourceFormat == SyndicationFormat.Rss20)
                    //    GetRss(item, feedData);

                    if (!string.IsNullOrEmpty(feedData.Content) && !string.IsNullOrEmpty(feedData.TitleAll))
                        feedList.Add(feedData);
                }
            }

            return feedList.OrderByDescending(x => x.DateArticle).ToList();
        }

        private void GetRss(SyndicationItem item, RssItem feedData) 
        {
            string content = string.Empty;
            feedData.Id = item.Id;
            feedData.Title = item.Title != null ? item.Title.Text.Length > 60 ? item.Title.Text.Substring(0, 60) + " ..." : item.Title.Text : "";
            feedData.TitleAll = item.Title != null ? item.Title.Text : "";
            feedData.Link = item.Links[0].Uri.AbsoluteUri;
            if (item.Content != null)
            {
                content = item.Content.Text;
                feedData.Content = HtmlUtilities.ConvertToText(item.Content.Text);
            }
            else if (item.Summary != null)
            {
                content = item.Summary.Text;
                feedData.Content = HtmlUtilities.ConvertToText(item.Summary.Text);
            }

            
            // Si el año de la fecha es 1601, quiere decir que una fecha generada por el feed de RSS
            if (item.PublishedDate.Year != 1601)
            {
                feedData.DateArticle = item.PublishedDate.DateTime;
                feedData.Date = item.PublishedDate.ToString("dd/MM/yy HH:mm tt");
            }

            feedData.Image = string.Empty;
            foreach (SyndicationLink link in item.Links)
            {
                string linkImagen = link.Uri.AbsoluteUri.ToLower();
                if (linkImagen.Contains(".jpg") || linkImagen.Contains(".jpeg") || linkImagen.Contains(".png") || linkImagen.Contains(".gif"))
                    feedData.Image = link.Uri.AbsoluteUri;
            }

            if (string.IsNullOrEmpty(feedData.Image))
                foreach (ISyndicationNode link in item.ElementExtensions)
                {
                    string linkImagen = link.NodeValue.ToLower();
                    if (linkImagen.Contains(".jpg") || linkImagen.Contains(".jpeg") || linkImagen.Contains(".png") || linkImagen.Contains(".gif"))
                    {
                        if (linkImagen.Contains("<img "))
                            feedData.Image = GetImage(link.NodeValue);
                        else
                            feedData.Image = link.NodeValue;
                    }
                }

            if (string.IsNullOrEmpty(feedData.Image))
                feedData.Image = GetImage(content);

            if (string.IsNullOrEmpty(category))
            {
                if (item.Categories.Count > 0)
                {
                    string cat = item.Categories.FirstOrDefault().NodeValue.ToLower();
                    if (!string.IsNullOrEmpty(cat))
                        feedData.Category = char.ToUpper(cat[0]) + cat.Substring(1);
                    else
                        feedData.Category = "Otros";
                }
                else
                    feedData.Category = "Otros";
            }
            else
                feedData.Category = category;

            feedData.ExpirationDate = DateTime.Now.AddMinutes(cacheUpdateMinutes);
            feedData.IsRead = false;
        }

        private List<string> FetchLinksFromSource(string htmlSource)
        {
            List<string> links = new List<string>();
            string regexImgSrc = @"<img[^>]*?src\s*=\s*[""']?([^'"" >]+?)[ '""][^>]*?>";
            MatchCollection matchesImgSrc = Regex.Matches(htmlSource, regexImgSrc, RegexOptions.IgnoreCase | RegexOptions.Singleline);
            foreach (Match m in matchesImgSrc)
            {
                string href = m.Groups[1].Value;
                links.Add(href);
            }
            return links;
        }

        private string GetImage(string text)
        {
            string img = FetchLinksFromSource(text).FirstOrDefault();
            if (!string.IsNullOrEmpty(img))
            {
                if (!img.StartsWith("http://") && !img.StartsWith("https://"))
                {
                    if (img.StartsWith("//"))
                        img = "http:" + img;
                    else
                        img = "http://" + img;
                }
            }

            return img;
        }

        private async Task<string> ReadFileContent(string path)
        {
            StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
            StorageFile file = await folder.GetFileAsync(path);
            return await Windows.Storage.FileIO.ReadTextAsync(file);
        }

        private bool IsLocalPath(string p)
        {
            Uri uri = null;
            if (Uri.TryCreate(p, UriKind.Absolute, out uri))
                return uri.IsFile;
            else
                return true;
        }
    }
}
